package com.chat.handler;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.chat.domain.pojo.Group;
import com.chat.domain.pojo.Message;
import com.chat.domain.pojo.User;
import com.chat.service.GroupService;
import com.chat.service.MessageService;
import com.chat.service.UserService;
import com.chat.util.CommonUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author wb
 * @since 2024-08-13
 */

@Slf4j
public class ChatWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private final UserService userService = SpringUtil.getBean(UserService.class);

    private final GroupService groupService = SpringUtil.getBean(GroupService.class);

    private final MessageService messageService = SpringUtil.getBean(MessageService.class);

    /**
     * 存储已经登录用户的channel对象
     */
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 存储用户id和用户的channelId绑定
     */
    public static ConcurrentHashMap<Integer, ChannelId> userMap = new ConcurrentHashMap<>();
    /**
     * 用于存储群聊房间号和群聊成员的channel信息
     */
    public static ConcurrentHashMap<Integer, ChannelGroup> groupMap = new ConcurrentHashMap<>();


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("{} 用户上线了", ctx.channel());

        // 添加到 channelGroup 中
        channelGroup.add(ctx.channel());

        ctx.channel().id();
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("{} 用户离线了", ctx.channel());

        // 查找并移除用户ID
        Integer offlineUserId = null;
        for (Integer userId : userMap.keySet()) {
            if (userMap.get(userId).equals(ctx.channel().id())) {
                offlineUserId = userId;
                break;
            }
        }

        if (offlineUserId != null) {
            userMap.remove(offlineUserId);
            broadcastUserStatus(offlineUserId, 1);
        }

        // 移除用户ID和 channelId 的绑定
        channelGroup.remove(ctx.channel());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Integer userId = null;

        // 首次连接是FullHttpRequest，把用户id和对应的channel对象存储起来
        if (msg instanceof FullHttpRequest request) {

            // 获取 url 中的参数
            String uri = request.uri();
            userId = CommonUtil.getUrlParams(uri);

            if (userId != null) {
                // 存储用户ID和 channelId 的绑定
                userMap.put(userId, ctx.channel().id());

                // 广播自己上线
                broadcastUserStatus(userId, 0);

                // 初始好友状态
                initUserFriendStatus(userId, ctx);

                // 第1次登录,获取用户加入的群聊加入到 群聊组 中
                initUserToGroup(userId, ctx);

                // 如果url包含参数，需要处理
                if (uri.contains("?")) {
                    String newUri = uri.substring(0, uri.indexOf("?"));
                    request.setUri(newUri);
                }
            }

        } else if (msg instanceof TextWebSocketFrame frame) {
            log.info("客户端收到服务器数据：{}", frame.text());
            //正常的TEXT消息类型
            Message message = JSON.parseObject(frame.text(), Message.class);
            String chatId = message.getChatId();

            int receiverId = Integer.parseInt(chatId.substring(chatId.lastIndexOf("-") + 1));

            // 处理群聊消息
            if (chatId.contains("group")) {
                log.info("群聊成员: {} 群聊消息: {}", groupMap, message);

                // 推送群聊信息
                groupMap.get(receiverId).writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(message)));
            }

            // 处理好友消息
            else {
                String[] parts = chatId.split("-");
                Integer user1 = Integer.parseInt(parts[1]);
                int user2 = Integer.parseInt(parts[2]);
                receiverId = Objects.equals(message.getSendId(), user1) ? user2 : user1;

                // 处理私聊的消息
                ChannelId channelId = userMap.get(receiverId);

                if (channelId != null) {
                    // 获取对应的channel
                    Channel ct = channelGroup.find(channelId);
                    if (ct != null) {
                        log.info("好友消息: {}", message);
                        ct.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(message)));
                    }
                }
            }

            // 保存消息
            messageService.save(message);
        }
        super.channelRead(ctx, msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {

    }


    /**
     * 广播上线离线消息
     *
     * @param userId 用户ID
     * @param status 状态
     */
    private void broadcastUserStatus(Integer userId, Integer status) {
        // 构建消息
        JSONObject message = new JSONObject();
        message.put("chatType", "status");
        message.put("userId", userId);
        message.put("status", status);
        String jsonStr = message.toJSONString();

        // 遍历所有在线用户
        channelGroup.forEach(channel -> {
            if (channel.isActive()) {
                channel.writeAndFlush(new TextWebSocketFrame(jsonStr));
            }
        });

        log.info("广播用户状态-->{}", jsonStr);
    }

    /**
     * 广播群聊在线人数
     * @param groupId
     */
    private void broadcastGroupOnlineCount(Integer groupId){
        JSONObject message = new JSONObject();
        message.put("chatType", "group_online");
        message.put("groupId", groupId);
        message.put("onlineCount", groupMap.get(groupId).size());
        log.info("广播群聊在线人数-->{}", message);
        ChannelGroup group = groupMap.get(groupId);
        if (group != null) {
            group.writeAndFlush(new TextWebSocketFrame(message.toJSONString()));
        }
    }

    /**
     * 初始化用户好友状态
     *
     * @param userId 用户Id
     * @param ctx    ChannelHandlerContext
     */
    private void initUserFriendStatus(Integer userId, ChannelHandlerContext ctx) {
        // 查询用户好友
        List<User> friends = userService.getFriendsByUserId(userId);

        // 构建消息
        JSONObject statusMsg = new JSONObject();
        statusMsg.put("chatType", "init_status");
        JSONArray list = new JSONArray();
        for (User friend : friends) {
            Integer friendId = friend.getId();
            if (userMap.containsKey(friendId)) {
                JSONObject obj = new JSONObject();
                obj.put("userId", friendId);
                obj.put("status", 0);
                list.add(obj);
            }
        }
        statusMsg.put("friends", list);

        // 发送给当前登录用户
        ctx.channel().eventLoop().schedule(() -> {
            ctx.channel().writeAndFlush(new TextWebSocketFrame(statusMsg.toJSONString()));
        }, 500, TimeUnit.MILLISECONDS); // 延迟 500ms 再发送

        log.info("初始化好友状态消息: {}", statusMsg);
    }


    /**
     * 将用户初始化到加入的群聊组中
     *
     * @param userId 用户Id
     * @param ctx    ChannelHandlerContext
     */
    private void initUserToGroup(Integer userId, ChannelHandlerContext ctx) {
        // 查询用户加入的群聊
        List<Group> groups = groupService.getGroupsByUserId(userId);
        groups.forEach(group -> {
            Integer groupId = group.getId();
            // 获取群聊的 channelGroup
            ChannelGroup channelGroup = groupMap.get(groupId);
            if (channelGroup == null) {
                channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
                groupMap.put(groupId, channelGroup);
            }
            channelGroup.add(ctx.channel());

            // 广播群聊在线人数
            ctx.channel().eventLoop().schedule(() -> {
                broadcastGroupOnlineCount(groupId);
            }, 500, TimeUnit.MILLISECONDS); // 延迟 500ms 再发送
        });

    }
}
